using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Bcpg;


namespace Pbdms.Common.CryptoUtils
{
    public class Signer
    {
        
        #region version 2
        public void SignFile(string fileIn, PgpSecretKey secretKey, PgpPrivateKey pgpPrivKey, Stream outputStream1, bool compress)
        {
            FileStream fIn = null;
            Stream lOut = null;
            PgpLiteralDataGenerator lGen = null;
            BcpgOutputStream bOut = null;
            PgpCompressedDataGenerator cGen = null;
            Stream cOut = null;
           
            FileInfo file = new FileInfo(fileIn);
            Stream outputStream = null;
            try
            {
                fIn = file.OpenRead();
                outputStream = new ArmoredOutputStream(outputStream1);
                PgpSignatureGenerator sGen = new PgpSignatureGenerator(secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
                sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
                foreach (string userId in secretKey.PublicKey.GetUserIds())
                {
                    PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
                    spGen.SetSignerUserId(false, userId);
                    sGen.SetHashedSubpackets(spGen.Generate());
                    // Just the first one!
                    break;
                }

                cOut = outputStream;
                if (compress)
                {
                    cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);

                    cOut = cGen.Open(cOut);
                }

                bOut = new BcpgOutputStream(cOut);
                sGen.GenerateOnePassVersion(false).Encode(bOut);

                lGen = new PgpLiteralDataGenerator();
                lOut = lGen.Open(bOut, PgpLiteralData.Binary, file);

                int ch = 0;

                while ((ch = fIn.ReadByte()) >= 0)
                {
                    lOut.WriteByte((byte)ch);
                    sGen.Update((byte)ch);
                }

                sGen.Generate().Encode(bOut);



            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                fIn.Close();
                lOut.Close();
                lGen.Close();
                bOut.Close();

                if (cGen != null)
                {
                    cGen.Close();
                }
                cOut.Close();
                outputStream.Close();
            }
        }
        public PgpPublicKey SignPublicKey(PgpPrivateKey pgpPrivKey,PgpSecretKey secretKey,PgpPublicKey keyToBeSigned, string notationName, string notationValue)
        {
            Stream os = new MemoryStream();
            os = new ArmoredOutputStream(os);




            PgpSignatureGenerator sGen = new PgpSignatureGenerator(
                secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
            //pass in the private key and the signature type
            sGen.InitSign(PgpSignature.DirectKey, pgpPrivKey);

            BcpgOutputStream bOut = new BcpgOutputStream(os);

            sGen.GenerateOnePassVersion(false).Encode(bOut);
            os.Close();
            PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();

            bool isHumanReadable = true;
            spGen.SetNotationData(true, isHumanReadable, notationName, notationValue);

            PgpSignatureSubpacketVector packetVector = spGen.Generate();
            sGen.SetHashedSubpackets(packetVector);

            bOut.Flush();


            return (PgpPublicKey.AddCertification(keyToBeSigned, sGen.Generate()));
        }

        public string SignAMessage(string message, PgpSecretKey secretKey, PgpPrivateKey pgpPrivKey)
        {
            Stream fIn = null;
            Stream lOut = null;
            PgpLiteralDataGenerator lGen = null;
            BcpgOutputStream bOut = null;
            PgpCompressedDataGenerator cGen = null;
            Stream cOut = null;
            

            Stream outputStream = null;
            Stream fileOut = null;
            Stream memTemp = null;


            //pgpPrivKey = secretKey.ExtractPrivateKey("3".ToCharArray());
            //convert the message to an location in a memory
            Byte[] b = System.Text.Encoding.ASCII.GetBytes(message);

            memTemp = new MemoryStream();
            int count = 0;
            while (count < b.Length)
            {
                // change to stream
                memTemp.WriteByte(b[count]);
                count++;
            }
            memTemp.Seek(0, SeekOrigin.Begin);
            try
            {
                fIn = memTemp;
                fileOut = new MemoryStream();
                outputStream = new ArmoredOutputStream(fileOut);
                PgpSignatureGenerator sGen = new PgpSignatureGenerator(secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
                sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
                foreach (string userId in secretKey.PublicKey.GetUserIds())
                {
                    PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
                    spGen.SetSignerUserId(false, userId);
                    sGen.SetHashedSubpackets(spGen.Generate());
                    // Just the first one!
                    break;
                }

                cOut = outputStream;

                cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);

                cOut = cGen.Open(cOut);


                bOut = new BcpgOutputStream(cOut);
                sGen.GenerateOnePassVersion(false).Encode(bOut);

                lGen = new PgpLiteralDataGenerator();
                lOut = lGen.Open(bOut, PgpLiteralData.Binary, "Message passing", memTemp.Length, DateTime.Now);

                int ch = 0;

                while ((ch = fIn.ReadByte()) >= 0)
                {
                    lOut.WriteByte((byte)ch);
                    sGen.Update((byte)ch);
                }

                sGen.Generate().Encode(bOut);



            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                fIn.Close();
                lOut.Close();
                lGen.Close();
                bOut.Close();

                if (cGen != null)
                {
                    cGen.Close();
                }
                cOut.Close();
                outputStream.Close();

                memTemp.Close();
            }
            fileOut.Seek(0, SeekOrigin.Begin);

            int start2 = 0;
            byte[] newBytes2 = new byte[fileOut.Length];
            while (start2 < fileOut.Length)
            {
                newBytes2[start2] = (byte)fileOut.ReadByte();
                start2++;
            }
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            string str = enc.GetString(newBytes2);
            fileOut.Close();
            return str;
        }
        public string SignADetachedSignature(string message,PgpSecretKey pgpSec, PgpPrivateKey pgpPrivKey)
        {
            //convert the message to an location in a memory
            Byte[] b = System.Text.Encoding.ASCII.GetBytes(message);
            MemoryStream memTemp = null;
            memTemp = new MemoryStream();
            int count = 0;
            while (count < b.Length)
            {
                // change to stream
                memTemp.WriteByte(b[count]);
                count++;
            }
            memTemp.Seek(0, SeekOrigin.Begin);
            Stream fIn = memTemp;
            Stream fileOut = new MemoryStream();
            Stream outputStream = new ArmoredOutputStream(fileOut);
            
            
            PgpSignatureGenerator sGen = new PgpSignatureGenerator(
                pgpSec.PublicKey.Algorithm, HashAlgorithmTag.Sha1);

            sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
            
            BcpgOutputStream bOut = new BcpgOutputStream(outputStream);

            
            int ch;
            while ((ch = fIn.ReadByte()) >= 0)
            {
                sGen.Update((byte)ch);
            }
            fIn.Close();

            sGen.Generate().Encode(bOut);

 
            fIn.Close();
            bOut.Close();
            outputStream.Close();
            memTemp.Close();            
            fileOut.Seek(0, SeekOrigin.Begin);
            int start2 = 0;
            byte[] newBytes2 = new byte[fileOut.Length];
            while (start2 < fileOut.Length)
            {
                newBytes2[start2] = (byte)fileOut.ReadByte();
                start2++;
            }
            System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            string str = enc.GetString(newBytes2);
            fileOut.Close();
            return str;
         
        }
        public PgpPublicKey removeSignature(PgpPublicKey keyTobeRemoved, PgpPublicKey mykey)
        {
            ArrayList signTobeRemoved = new ArrayList();
            foreach (PgpSignature sign in keyTobeRemoved.GetSignaturesOfType(PgpSignature.DirectKey))
            {
                Verifier verifier = new Verifier();
                Boolean check = verifier.VerifySignature(sign, mykey, keyTobeRemoved.KeyId.ToString("X"));
                ///if it belong to the signature of mykey
                if (check == true)
                {
                    signTobeRemoved.Add(sign);
                }
                else
                {

                }
            }
            PgpPublicKey newkey = keyTobeRemoved;

            foreach(PgpSignature mySig in signTobeRemoved)
            {

                newkey=PgpPublicKey.RemoveKeySig(newkey, mySig);
            }
            return newkey;
        }
        #endregion

        /// <summary>
        /// sign a encrypte memory
        /// </summary>
        /// <param name="message"></param>
        /// <param name="secretKey"></param>
        /// <param name="pgpPrivKey"></param>
        /// <returns></returns>
        public MemoryStream SignMemory(MemoryStream bytesToSign, PgpSecretKey secretKey, PgpPrivateKey pgpPrivKey)
        {
            Stream fIn = null;
            Stream lOut = null;
            PgpLiteralDataGenerator lGen = null;
            BcpgOutputStream bOut = null;
            PgpCompressedDataGenerator cGen = null;
            Stream cOut = null;


            Stream outputStream = null;
            MemoryStream fileOut = null;
           


        
            try
            {
                fIn = bytesToSign;
                fileOut = new MemoryStream();
                outputStream = new ArmoredOutputStream(fileOut);
                PgpSignatureGenerator sGen = new PgpSignatureGenerator(secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
                sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
                foreach (string userId in secretKey.PublicKey.GetUserIds())
                {
                    PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
                    spGen.SetSignerUserId(false, userId);
                    sGen.SetHashedSubpackets(spGen.Generate());
                    // Just the first one!
                    break;
                }

                cOut = outputStream;

                cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);

                cOut = cGen.Open(cOut);


                bOut = new BcpgOutputStream(cOut);
                sGen.GenerateOnePassVersion(false).Encode(bOut);

                lGen = new PgpLiteralDataGenerator();
                lOut = lGen.Open(bOut, PgpLiteralData.Binary, "Message passing", bytesToSign.Length, DateTime.Now);

                int ch = 0;

                while ((ch = fIn.ReadByte()) >= 0)
                {
                    lOut.WriteByte((byte)ch);
                    sGen.Update((byte)ch);
                }

                sGen.Generate().Encode(bOut);



            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                fIn.Close();
                lOut.Close();
                lGen.Close();
                bOut.Close();
                bytesToSign.Close();
                if (cGen != null)
                {
                    cGen.Close();
                }
                cOut.Close();
                outputStream.Close();

               
            }
            fileOut.Seek(0, SeekOrigin.Begin);

       
            return fileOut;
        }
        public void SignFile(string fileOut, string fileIn, PgpSecretKey secretKey, PgpPrivateKey pgpPrivKey, bool compress)
        {
            FileStream fIn = null;
            Stream lOut = null;
            PgpLiteralDataGenerator lGen = null;
            BcpgOutputStream bOut = null;
            PgpCompressedDataGenerator cGen = null;
            Stream cOut = null;
            Stream outputStreamtemp = null;
            FileInfo file = new FileInfo(fileIn);
            Stream outputStream = null;
            try
            {
                fIn = file.OpenRead();
                outputStreamtemp = File.Create(fileOut);
                outputStream = new ArmoredOutputStream(outputStreamtemp);
                PgpSignatureGenerator sGen = new PgpSignatureGenerator(secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
                sGen.InitSign(PgpSignature.BinaryDocument, pgpPrivKey);
                foreach (string userId in secretKey.PublicKey.GetUserIds())
                {
                    PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
                    spGen.SetSignerUserId(false, userId);
                    sGen.SetHashedSubpackets(spGen.Generate());
                    // Just the first one!
                    break;
                }

                cOut = outputStream;
                if (compress)
                {
                    cGen = new PgpCompressedDataGenerator(CompressionAlgorithmTag.ZLib);

                    cOut = cGen.Open(cOut);
                }

                bOut = new BcpgOutputStream(cOut);
                sGen.GenerateOnePassVersion(false).Encode(bOut);

                lGen = new PgpLiteralDataGenerator();
                lOut = lGen.Open(bOut, PgpLiteralData.Binary, file);

                int ch = 0;

                while ((ch = fIn.ReadByte()) >= 0)
                {
                    lOut.WriteByte((byte)ch);
                    sGen.Update((byte)ch);
                }

                sGen.Generate().Encode(bOut);



            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                fIn.Close();
                lOut.Close();
                lGen.Close();
                bOut.Close();

                if (cGen != null)
                {
                    cGen.Close();
                }
                cOut.Close();
                outputStream.Close();
                if (outputStreamtemp != null)
                {
                    outputStreamtemp.Close();
                }
            }
        }
    }
}
